home *** CD-ROM | disk | FTP | other *** search
/ Shareware Grab Bag / Shareware Grab Bag.iso / 007 / floatpak.arc / FLOATPAK.TXT
Text File  |  1988-09-18  |  21KB  |  964 lines

  1.  
  2. /*
  3. ** Floating-point library package.
  4. ** R.E. Grehan - BYTE Magazine
  5. **
  6. ** What you've got here is a floating-point four-banger for the
  7. ** 8088/80286/80386 family.  The routines perform:
  8. ** fpadd -- addition
  9. ** fpmult - multiplication
  10. ** fpsub -- subtraction
  11. ** fpdiv -- division
  12. ** fpinput - input (converts string to floating-point)
  13. ** fpoutput - output (converts floating-point to string)
  14. **
  15. ** This is not an "industrial strength" package, at least
  16. ** not in terms of optimization (although I have unwound
  17. ** a lot of loops).
  18. ** The C interface routines are written (of course) for BYTE
  19. ** Small C -- you should be able to use them as interfacing
  20. ** guides for the actual assembly-language floating-point
  21. ** routines.
  22. ** In the input and output routines, I've added comments in
  23. ** spots where the code could be modified to allow for freer
  24. ** formats (like, no preceding '+' sign, for example).
  25. ** See my articles in the September and October 1988 BYTEs
  26. ** for more details.
  27. ** Enjoy.
  28. ** --Rick Grehan
  29. **
  30. ** REFERENCES:
  31. ** Microprocessor Programming for Computer Hobbyists by Neill Graham
  32. ** Tab books, 1977
  33. **
  34. ** Scelbi 8080 Software Gourmet Guide and Cook Book by Robert Findley
  35. ** Scelbi Computer Consulting, 1976
  36. */
  37.  
  38. /*
  39. ** Add two floating-point numbers.
  40. ** flt1, flt2, and flt3 are 10-byte arrays holding floating-point
  41. ** numbers.
  42. ** This function performs: flt1+flt2 --> flt3.
  43. ** The function returns 0 if ok, else error code.
  44. */
  45. fpadd(flt1,flt2,flt3)
  46. char flt1[10], flt2[10], flt3[10];
  47. {
  48. #asm
  49.     MOV    BP,SP
  50. ;First move flt1 and flt2 into FAC1 and FAC2
  51.     MOV    SI,6[BP]
  52.     MOV    DI,OFFSET FAC1_SIGN
  53.     CALL    LDFACC
  54.     MOV    SI,4[BP]
  55.     MOV    DI,OFFSET FAC2_SIGN
  56.     CALL    LDFACC
  57. ;Perform the addition
  58.     CALL    FPADD
  59. ;Make sure everything was ok.
  60.     OR    BX,BX
  61.     JZ    _FPADD1
  62.     XOR    CX,CX        ;For small C
  63.     RET
  64. ;Return the result
  65. _FPADD1:
  66.     MOV    DI,2[BP]
  67.     CALL    STFAC1
  68.     XOR    CX,CX        ;For small C
  69. #endasm
  70. }
  71.  
  72. /*
  73. ** Multiply two floating-point numbers.
  74. ** flt1, flt2, and flt3 are 10-byte arrays holding floating-point
  75. ** numbers.
  76. ** This function performs: flt1*flt2 --> flt3.
  77. ** The function returns 0 if ok, else error code.
  78. */
  79. fpmult(flt1,flt2,flt3)
  80. char flt1[10], flt2[10], flt3[10];
  81. {
  82. #asm
  83.     MOV    BP,SP
  84. ;First move flt1 and flt2 into FAC1 and FAC2
  85.     MOV    SI,6[BP]
  86.     MOV    DI,OFFSET FAC1_SIGN
  87.     CALL    LDFACC
  88.     MOV    SI,4[BP]
  89.     MOV    DI,OFFSET FAC2_SIGN
  90.     CALL    LDFACC
  91. ;Perform the multiplication
  92.     CALL    FPMULT
  93. ;Make sure everything was ok.
  94.     OR    BX,BX
  95.     JZ    _FPMUL1
  96.     XOR    CX,CX        ;For small C
  97.     RET
  98. ;Return the result
  99. _FPMUL1:
  100.     MOV    DI,2[BP]
  101.     CALL    STFAC1
  102.     XOR    CX,CX        ;For small C
  103. #endasm
  104. }
  105.  
  106.  
  107. /*
  108. ** Subtract two floating-point numbers.
  109. ** flt1, flt2, and flt3 are 10-byte arrays holding floating-point
  110. ** numbers.
  111. ** This function performs: flt1-flt2 --> flt3.
  112. ** The function returns 0 if ok, else error code.
  113. */
  114. fpsub(flt1,flt2,flt3)
  115. char flt1[10], flt2[10], flt3[10];
  116. {
  117. #asm
  118.     MOV    BP,SP
  119. ;First move flt1 and flt2 into FAC1 and FAC2
  120.     MOV    SI,6[BP]
  121.     MOV    DI,OFFSET FAC1_SIGN
  122.     CALL    LDFACC
  123.     MOV    SI,4[BP]
  124.     MOV    DI,OFFSET FAC2_SIGN
  125.     CALL    LDFACC
  126. ;Perform the subtraction
  127.     CALL    FPSUB
  128. ;Make sure everything was ok.
  129.     OR    BX,BX
  130.     JZ    _FPSUB1
  131.     XOR    CX,CX        ;For small c
  132.     RET
  133. ;Return the result
  134. _FPSUB1:
  135.     MOV    DI,2[BP]
  136.     CALL    STFAC1
  137.     XOR    CX,CX        ;For small c
  138. #endasm
  139. }
  140.  
  141. /*
  142. ** Divide floating-point numbers.
  143. ** flt1, flt2, and flt3 are 10-byte arrays holding floating-point
  144. ** numbers.
  145. ** This function performs: flt1/flt2 --> flt3.
  146. ** The function returns 0 if ok, else error code.
  147. */
  148. fpdiv(flt1,flt2,flt3)
  149. char flt1[10], flt2[10], flt3[10];
  150. {
  151. #asm
  152.     MOV    BP,SP
  153. ;First move flt1 and flt2 into FAC1 and FAC2
  154.     MOV    SI,6[BP]
  155.     MOV    DI,OFFSET FAC1_SIGN
  156.     CALL    LDFACC
  157.     MOV    SI,4[BP]
  158.     MOV    DI,OFFSET FAC2_SIGN
  159.     CALL    LDFACC
  160. ;Perform the division
  161.     CALL    FPDIV
  162. ;Make sure everything was ok.
  163.     OR    BX,BX
  164.     JZ    _FPDIV1
  165.     XOR    CX,CX        ;For small c
  166.     RET
  167. ;Return the result
  168. _FPDIV1:
  169.     MOV    DI,2[BP]
  170.     CALL    STFAC1
  171.     XOR    CX,CX        ;For small c
  172. #endasm
  173. }
  174.  
  175. /*
  176. ** Input a floating-point number.
  177. ** Accepts pointer to a string that must be in the
  178. ** form: sd.ddddEsdd<null>
  179. ** Also accepts pointer to an array of 10 bytes, into which
  180. ** the floating-point number is stored.
  181. */
  182. fpinput(str,fnum)
  183. char *str;
  184. char fnum[10];
  185. {
  186. #asm
  187.     MOV    BP,SP
  188. ;Put pointer to string in SI and call conversion routine
  189.     MOV    SI,4[BP]
  190.     CALL    FPINP
  191. ;Move the result into fnum
  192.     MOV    DI,2[BP]
  193.     CALL    STFAC1
  194.     XOR    CX,CX        ;For small c
  195. #endasm
  196. }
  197.  
  198. /*
  199. ** Output a floating-point number.
  200. ** Accepts pointer to a 10-byte array containing an floating-
  201. ** point number.  Also accepts pointer to a buffer, into
  202. ** which the number is stored in the format:
  203. **  sd.ddddEsdd<null>
  204. ** Number of digits to the right of the decimal place is
  205. ** specified in n.
  206. */
  207. fpoutput(fnum,buff,n)
  208. char fnum[10];
  209. char *buff;
  210. int n;
  211. {
  212. #asm
  213.     MOV    BP,SP
  214. ;First move the number into FAC1
  215.     MOV    SI,6[BP]
  216.     MOV    DI,OFFSET FAC1_SIGN
  217.     CALL    LDFACC
  218. ;Do output
  219.     MOV    CX,2[BP]
  220.     MOV    DI,4[BP]
  221.     CALL    FPOUT
  222.     XOR    CX,CX        ;For small c
  223. #endasm
  224. }
  225.  
  226. /*
  227. ** Actual floating-point routines follow.
  228. */
  229.  
  230. #asm
  231.  
  232. ;
  233. ; Definitions for the floating-point accumulators
  234. ;
  235. FAC1_SIGN DB ?  ;FP ACCUM1 - SIGN (80h=negative, 0=positive)
  236. FAC1_EXP DW ?          ;  - EXPONENT
  237. FAC1_MAN DW 5 DUP (?)  ;  - MANTISSA
  238.  
  239. FAC2_SIGN DB ?        ;Accumulator 2
  240. FAC2_EXP DW ?
  241. FAC2_MAN DW 5 DUP (?)
  242.  
  243. ;Third accumulator for mantissa only - used as a temp location for
  244. ;calculations.
  245. FAC3_MAN DW 5 DUP (?)
  246.  
  247. EXP_SIGN DB    ?    ;Sign of decimal exponent
  248.  
  249. ;Equates
  250. BIAS    EQU    16384    ;Bias for exponent
  251. MAXEXP    EQU    32768    ;Maximum exponent
  252. ;Error codes returned
  253. DIVZER    EQU    1    ;Divide by zero error
  254. EXPOVF    EQU    2    ;Exponent overflow error
  255. EXPUND    EQU    3    ;Exponent underflow error
  256.  
  257.  
  258.  
  259. ;**********
  260. ;Load a floating-point accumulator.
  261. ;Assumes SI points to a floating-point number and DI points
  262. ;to the _SIGN field of the accumulator to load.
  263.     PUBLIC LDFACC
  264. LDFACC:
  265.     CLD        ;Set direction to increment
  266. ;Get the sign bit
  267.     MOV    AX,[SI]
  268.     AND    AH,80H
  269.     MOV    [DI],AH
  270.     INC    DI
  271. ;Move the rest
  272.     MOV    BX,DI    ;Save destination location
  273.     MOV    CX,5
  274.     REP    MOVSW
  275.     MOV    WORD PTR [DI],0
  276. ;Clear sign bit from exponent
  277.     AND    WORD PTR [BX],7FFFH
  278.     INC    BX
  279.     INC    BX
  280. ;Shift the mantissa right 1 bit
  281.     SHR    WORD PTR [BX],1
  282.     RCR    WORD PTR 2[BX],1
  283.     RCR    WORD PTR 4[BX],1
  284.     RCR    WORD PTR 6[BX],1
  285.     RCR    WORD PTR 8[BX],1
  286. LDFACC1:
  287.     RET
  288.  
  289. ;**********
  290. ;Store FACC1 to the memory location
  291. ;pointed to by DI.
  292. ;NOTE: THIS ROUTINE MUNGES FAC1 IN THE PROCESS
  293. ; OF STORING IT TO MEMORY.  NOT A GOOD IDEA,
  294. ; PARTICULARLY IF YOU'RE DOING LOT'S OF BACK-
  295. ; TO-BACK COMPUTATIONS AND YOU WANT TO
  296. ; OPTIMIZE THINGS.  (I just thought I should
  297. ; bring this to your attention.--rg)
  298.     PUBLIC STFAC1
  299. STFAC1:
  300. ;Set direction
  301.     CLD
  302. ;Shift the mantissa left 1 bit
  303.     SHL    FAC1_MAN+8,1
  304.     RCL    FAC1_MAN+6,1
  305.     RCL    FAC1_MAN+4,1
  306.     RCL    FAC1_MAN+2,1
  307.     RCL    FAC1_MAN,1
  308. ;Set the sign bit based on FAC1_SIGN
  309.     CMP    FAC1_SIGN,0
  310.     JNE    STFC1
  311.     AND    FAC1_EXP,7FFFH
  312.     JMP    SHORT STFC2
  313. STFC1:    OR    FAC1_EXP,8000H
  314. STFC2:
  315. ;Move it all over
  316.     MOV    SI,OFFSET FAC1_EXP
  317.     MOV    CX,5
  318.     REP    MOVSW
  319.     RET
  320.  
  321. ;**********
  322. ;Floating-point add routine
  323. ;Arguments assumed to be in FAC1
  324. ; and FAC2.  Result in FAC1
  325.     PUBLIC FPADD
  326. FPADD:
  327. ;Determine which argument has the
  328. ;larger exponent.  Move the number with the
  329. ;larger exponent into FAC1 and the number with the
  330. ;smaller exponent into FAC2.
  331.     MOV    AX,FAC1_EXP
  332.     CMP    AX,FAC2_EXP
  333.     JAE    FADD1
  334.     MOV    CX,11
  335.     MOV    SI,OFFSET FAC1_SIGN
  336.     MOV    DI,OFFSET FAC2_SIGN
  337. FADD0:    MOV    AL,[SI]
  338.     XCHG    AL,[DI]
  339.     MOV    [SI],AL
  340.     INC    SI
  341.     INC    DI
  342.     LOOP    FADD0
  343. ;Now figure out how many bits to
  344. ;shift the mantissa of FAC2 in order
  345. ;to align the radix point.  If this
  346. ;amount is greater than 78, we can
  347. ;go home, cause the result is already
  348. ;in FAC1.
  349. FADD1:    MOV    AX,FAC1_EXP
  350.     SUB    AX,FAC2_EXP
  351.     CMP    AX,78
  352.     JL    FADD2
  353.     XOR    BX,BX        ;Show all ok.
  354.     RET
  355. ;See if the signs of FAC1 and
  356. ;FAC2 differ.  If so, negate the
  357. ;mantissa of FAC2.
  358. FADD2:    MOV    BL,FAC1_SIGN
  359.     CMP    BL,FAC2_SIGN
  360.     JZ    FADD3
  361.     MOV    BX,OFFSET FAC2_MAN
  362.     CALL    NEGBX
  363. ;Now shift FAC2 right by amount
  364. ;given in AX (which is the difference in the
  365. ;exponent amounts).
  366. FADD3:    MOV    CX,AX
  367.     OR    CX,CX        ;Shift necessary?
  368.     JZ    FADD4A
  369. FADD4:    SHR    FAC2_MAN,1
  370.     RCR    FAC2_MAN+2,1
  371.     RCR    FAC2_MAN+4,1
  372.     RCR    FAC2_MAN+6,1
  373.     RCR    FAC2_MAN+8,1
  374.     LOOP    FADD4
  375. ;Add the two mantissas.
  376. FADD4A:
  377.     MOV    AX,FAC2_MAN+8
  378.     ADD    FAC1_MAN+8,AX
  379.     MOV    AX,FAC2_MAN+6
  380.     ADC    FAC1_MAN+6,AX
  381.     MOV    AX,FAC2_MAN+4
  382.     ADC    FAC1_MAN+4,AX
  383.     MOV    AX,FAC2_MAN+2
  384.     ADC    FAC1_MAN+2,AX
  385.     MOV    AX,FAC2_MAN
  386.     ADC    FAC1_MAN,AX
  387. ;See if the sign of the two operands
  388. ;differ. If so, and if the result
  389. ;of the addition was negative, negate
  390. ;the FAC1 mantissa and move the sign
  391. ;of FAC2 to FAC1.
  392.     MOV    AL,FAC1_SIGN
  393.     CMP    AL,FAC2_SIGN
  394.     JZ    FADD5
  395.     MOV    AX,FAC1_MAN
  396.     OR    AX,AX
  397.     JNS    FADD5
  398.     MOV    BX,OFFSET FAC1_MAN
  399.     CALL    NEGBX
  400.     MOV    AL,FAC2_SIGN
  401.     MOV    FAC1_SIGN,AL
  402. ;Now normalize the contents of FAC1
  403. FADD5:    JMP    NORM_FAC1
  404.  
  405. ;**********
  406. ;Floating-point subtract.
  407. ;Subtract contents of FAC2 from
  408. ;FAC1 and leave the result in FAC1.
  409. ;
  410.     PUBLIC FPSUB
  411. FPSUB:
  412. ;Flip the sign of FAC2
  413.     XOR    FAC2_SIGN,80H
  414. ;And do an ADD
  415.     JMP    FPADD
  416.  
  417. ;**********
  418. ;Floating-point multiply
  419. ;Multiply FAC1 and FAC2 - result in FAC1
  420.     PUBLIC FPMULT
  421. FPMULT:
  422. ;First set up the signs of the result.  Do this by
  423. ;performing an exclusive-OR on the signs of the operands
  424.     MOV    AL,FAC2_SIGN
  425.     XOR    FAC1_SIGN,AL
  426. ;Now calculate the exponent of the result
  427.     MOV    AX,FAC2_EXP
  428.     ADD    AX,FAC1_EXP
  429.     SUB    AX,BIAS+1
  430.     MOV    FAC1_EXP,AX
  431. ;Clear FAC3 to hold results
  432.     XOR    AX,AX
  433.     MOV    FAC3_MAN,AX
  434.     MOV    FAC3_MAN+2,AX
  435.     MOV    FAC3_MAN+4,AX
  436.     MOV    FAC3_MAN+6,AX
  437.     MOV    FAC3_MAN+8,AX
  438. ;Set up counter
  439.     MOV    CX,79
  440. ;Shift FAC1 and result 1 bit to the right.
  441. ;If the bit shifted out of FAC1 is a 1, add FAC2
  442. ;to the result.
  443. FMUL1:    SHR    FAC3_MAN,1
  444.     RCR    FAC3_MAN+2,1
  445.     RCR    FAC3_MAN+4,1
  446.     RCR    FAC3_MAN+6,1
  447.     RCR    FAC3_MAN+8,1
  448. ;
  449.     SHR    FAC1_MAN,1
  450.     RCR    FAC1_MAN+2,1
  451.     RCR    FAC1_MAN+4,1
  452.     RCR    FAC1_MAN+6,1
  453.     RCR    FAC1_MAN+8,1
  454.     JNC    FMUL2
  455.     MOV    AX,FAC2_MAN+8
  456.     ADD    FAC3_MAN+8,AX
  457.     MOV    AX,FAC2_MAN+6
  458.     ADC    FAC3_MAN+6,AX
  459.     MOV    AX,FAC2_MAN+4
  460.     ADC    FAC3_MAN+4,AX
  461.     MOV    AX,FAC2_MAN+2
  462.     ADC    FAC3_MAN+2,AX
  463.     MOV    AX,FAC2_MAN
  464.     ADC    FAC3_MAN,AX
  465. FMUL2:    LOOP    FMUL1
  466. ;Move the result into FAC1
  467. FMUL3:    MOV    SI,OFFSET FAC3_MAN
  468.     MOV    DI,OFFSET FAC1_MAN
  469.     MOV    CX,5
  470.     REP    MOVSW
  471. ;Exit via the normalization routine
  472.     JMP    NORM_FAC1
  473.  
  474. ;**********
  475. ;Floating-point divide
  476. ;Divide FAC1 by FAC2 - result in FAC1
  477.     PUBLIC FPDIV
  478. FPDIV:
  479. ;Verify that we are not trying to divide by zero.
  480. ;Do this by checking the exponent of FAC2.
  481.     MOV    AX,FAC2_EXP
  482.     OR    AX,AX
  483.     JNE    FDIV0
  484.     MOV    BX,DIVZER        ;Indicate error
  485.     RET                ;Go home
  486. ;Figure out the sign of the result
  487. FDIV0:    MOV    AL,FAC2_SIGN
  488.     XOR    FAC1_SIGN,AL
  489. ;And calculate the resulting exponent
  490.     MOV    AX,FAC1_EXP
  491.     SUB    AX,FAC2_EXP
  492.     ADD    AX,BIAS        ;Fix bias
  493.     MOV    FAC1_EXP,AX
  494. ;Now perform the division, using nonrestoring fraction
  495. ;division.  First, clear the result.
  496.     XOR    AX,AX
  497.     MOV    FAC3_MAN,AX
  498.     MOV    FAC3_MAN+2,AX
  499.     MOV    FAC3_MAN+4,AX
  500.     MOV    FAC3_MAN+6,AX
  501.     MOV    FAC3_MAN+8,AX
  502. ;Do an initial subtraction
  503.     MOV    AX,FAC2_MAN+8
  504.     SUB    FAC1_MAN+8,AX
  505.     MOV    AX,FAC2_MAN+6
  506.     SBB    FAC1_MAN+6,AX
  507.     MOV    AX,FAC2_MAN+4
  508.     SBB    FAC1_MAN+4,AX
  509.     MOV    AX,FAC2_MAN+2
  510.     SBB    FAC1_MAN+2,AX
  511.     MOV    AX,FAC2_MAN
  512.     SBB    FAC1_MAN,AX
  513. ;Set up loop count
  514.     MOV    CX,80
  515. FDIV1:
  516. ;Shift quotient left
  517.     SHL    FAC3_MAN+8,1
  518.     RCL    FAC3_MAN+6,1
  519.     RCL    FAC3_MAN+4,1
  520.     RCL    FAC3_MAN+2,1
  521.     RCL    FAC3_MAN,1
  522. ;Shift dividend left
  523.     SHL    FAC1_MAN+8,1
  524.     RCL    FAC1_MAN+6,1
  525.     RCL    FAC1_MAN+4,1
  526.     RCL    FAC1_MAN+2,1
  527.     RCL    FAC1_MAN,1
  528. ;If 1 bit shifted out of dividend (indicating that
  529. ; the result of the subtraction was negative, 
  530. ; restore by adding divisor back in.
  531.     JNC    FDIV2
  532.     MOV    AX,FAC2_MAN+8
  533.     ADD    FAC1_MAN+8,AX
  534.     MOV    AX,FAC2_MAN+6
  535.     ADC    FAC1_MAN+6,AX
  536.     MOV    AX,FAC2_MAN+4
  537.     ADC    FAC1_MAN+4,AX
  538.     MOV    AX,FAC2_MAN+2
  539.     ADC    FAC1_MAN+2,AX
  540.     MOV    AX,FAC2_MAN
  541.     ADC    FAC1_MAN,AX
  542.     JMP    SHORT FDIV3
  543. ;If 0 bit shifted out of dividend, continue process, but make
  544. ;sure you set a 1 bit in the quotient.
  545. FDIV2:    MOV    AX,FAC2_MAN+8
  546.     SUB    FAC1_MAN+8,AX
  547.     MOV    AX,FAC2_MAN+6
  548.     SBB    FAC1_MAN+6,AX
  549.     MOV    AX,FAC2_MAN+4
  550.     SBB    FAC1_MAN+4,AX
  551.     MOV    AX,FAC2_MAN+2
  552.     SBB    FAC1_MAN+2,AX
  553.     MOV    AX,FAC2_MAN
  554.     SBB    FAC1_MAN,AX
  555.     OR    FAC3_MAN+8,1    ;Set lo bit of quotient to 1
  556. FDIV3:    LOOP    FDIV1
  557. ;Move quotient back into FAC1 and normalize
  558. ; (borrow the code already in FMUL to do this.)
  559.     JMP    FMUL3
  560.  
  561. ;
  562. ;**********
  563. ;NORMALIZE FAC1
  564. ;Call this at the end of floating-point operations, and
  565. ;before you store the floating-point number.
  566. ;
  567.     PUBLIC NORM_FAC1
  568. NORM_FAC1:
  569. ;If FAC1's mantissa is zero, claer FAC1's exponent and set
  570. ;it's sign to 0 to indicate true zero
  571.     MOV    AX,FAC1_MAN
  572.     OR    AX,FAC1_MAN+2
  573.     OR    AX,FAC1_MAN+4
  574.     OR    AX,FAC1_MAN+6
  575.     OR    AX,FAC1_MAN+8
  576.     JNE    NORM0
  577.     MOV    FAC1_SIGN,AL
  578.     MOV    FAC1_EXP,AX
  579.     XOR    BX,BX        ;Show all ok
  580.     RET
  581. ;If we have a 1 in highmost bit - shift FAC1 to the right and
  582. ;increment exponent
  583. NORM0:    MOV    BX,FAC1_EXP
  584.     TEST    FAC1_MAN,8000H
  585.     JZ    NORM1
  586.     SHR    FAC1_MAN,1
  587.     RCR    FAC1_MAN+2,1
  588.     RCR    FAC1_MAN+4,1
  589.     RCR    FAC1_MAN+6,1
  590.     RCR    FAC1_MAN+8,1
  591.     INC    BX
  592. ;
  593. ;Shift FAC1 left until we get a 1 in next-to-highmost bit.
  594. ;Subtract 1 from FAC1's exponent each time we do a shift.
  595. NORM1:    TEST    FAC1_MAN,4000H
  596.     JNZ    NORM2
  597.     SHL    FAC1_MAN+8,1
  598.     RCL    FAC1_MAN+6,1
  599.     RCL    FAC1_MAN+4,1
  600.     RCL    FAC1_MAN+2,1
  601.     RCL    FAC1_MAN,1
  602.     DEC    BX
  603.     JMP    NORM1
  604. ;
  605. NORM2:
  606.     MOV    FAC1_EXP,BX
  607. ;Check to see if the exponent overflowed
  608.     CMP    BX,MAXEXP
  609.     JB    NORM3
  610.     MOV    BX,EXPOVF    ;Error code
  611.     RET
  612. ;Check to see if the exponent underflowed
  613. NORM3:    OR    BX,BX
  614.     JNE    NORM4
  615.     MOV    BX,EXPUND
  616.     RET
  617. NORM4:    XOR    BX,BX        ;Show all ok
  618.     RET
  619. ;
  620. ;Routine to negate the mantissa pointed
  621. ;to by BX.
  622. NEGBX:
  623.     MOV    CX,5
  624.     STC
  625. NEG1:    NOT    WORD PTR 8[BX]
  626.     ADC    WORD PTR 8[BX],0
  627.     DEC    BX
  628.     DEC    BX
  629.     LOOP    NEG1
  630.     RET
  631.  
  632. ;**********
  633. ;FLOATING-POINT INPUT ROUTINE
  634. ;Assume SI points to a string containing:
  635. ;   sX.XXXXXEsXXn
  636. ; Where s= + or -
  637. ;  X is 0-9
  638. ;  n is null
  639.     PUBLIC FPINP
  640. FPINP:
  641. ;First set the direction for increment
  642.     CLD
  643. ;Get the sign
  644.     LODSB
  645.     CMP    AL,'+'
  646.     JNE    INP1
  647.     MOV    AL,0
  648.     JMP    SHORT INP2
  649. INP1:    MOV    AL,80h
  650. INP2:    MOV    FAC1_SIGN,AL
  651. ;Clear CX to hold accumulated value of decimal exponent
  652.     XOR    CX,CX
  653. ;Clear FAC1
  654.     MOV    FAC1_MAN,CX
  655.     MOV    FAC1_MAN+2,CX
  656.     MOV    FAC1_MAN+4,CX
  657.     MOV    FAC1_MAN+6,CX
  658.     MOV    FAC1_MAN+8,CX
  659. ;Get the digit to the left of the decimal point and increment
  660. ;decimal exponent
  661.     LODSB
  662.     CALL    ADDIGIT
  663. ;Skip past the decimal point.
  664. ;**NOTE: Add code to verify format here.
  665.     LODSB
  666. ;Read digits to the right of the decimal point
  667. INP3:    LODSB
  668.     CMP    AL,'0'
  669.     JL    INP4    ;Jump if not digit (must be 'E')
  670.     CMP    AL,'9'
  671.     JG    INP4    ;Jump if not digit (must be 'E')
  672.     CALL    ADDIGIT
  673.     DEC    CX    ;Decr. decimal accumulator
  674.     JMP    INP3
  675. ;Skip past the 'E'.
  676. ;**NOTE: Add code to verify format here.
  677. INP4:
  678. ;Read in exponent sign.
  679.     LODSB
  680.     CMP    AL,'+'
  681.     JNE    INP5
  682.     MOV    AL,0    ;Must be negative sign
  683.     JMP    SHORT INP6
  684. INP5:    MOV    AL,80H
  685. INP6:    MOV    EXP_SIGN,AL
  686. ;Read in exponent digits
  687.     XOR    BX,BX    ;Use as accumulator
  688.     XOR    AX,AX
  689.     MOV    DL,10    ;Multiplier
  690. INP7:    LODSB
  691.     CMP    AL,'0'
  692.     JL    INP8    ;Jump if not digit (must be null)
  693.     CMP    AL,'9'
  694.     JG    INP8    ;Jump if not digit (must be goofup)
  695.     SUB    AL,'0'    ;Make it a true number
  696.     XCHG    BX,AX    ;Accumulated value in AX
  697.     MUL    DL
  698.     ADD    AX,BX    ;Add in digit
  699.     XCHG    BX,AX    ;Accumulated value back in BX
  700.     JMP    INP7
  701. ;Fix up the accumulated exponent in BX
  702. INP8:    MOV    AL,EXP_SIGN
  703.     OR    AL,AL
  704.     JNE    INP9
  705. ;Exponent sign is positive
  706.     ADD    CX,BX
  707.     JMP    SHORT INP9A
  708. ;Exponent sign is negative
  709. INP9:    SUB    CX,BX
  710. ;Normalize FAC1
  711. INP9A:    MOV    FAC1_EXP,BIAS+79
  712.     CALL    NORM_FAC1
  713. ;Load FAC2 with a floating point 10.0
  714.     XOR    AX,AX
  715.     MOV    FAC2_SIGN,AL
  716.     MOV    FAC2_EXP,BIAS+4
  717.     MOV    FAC2_MAN,5000H
  718.     MOV    FAC2_MAN+2,AX
  719.     MOV    FAC2_MAN+4,AX
  720.     MOV    FAC2_MAN+6,AX
  721.     MOV    FAC2_MAN+8,AX
  722. ;Examine the contents of CX (our decimal exponent).  If CX=-n, divide
  723. ;FAC1 by 10 n times.  If CX=+n, multiply FAC1 by n.
  724.     MOV    DX,CX
  725.     OR    DX,DX
  726.     JE    INP11        ;Jump if nothing to do.
  727.     JL    INP12
  728. ;Multiply
  729. INP10:    CALL    FPMULT
  730.     DEC    DX
  731.     JNE    INP10
  732. INP11:    RET
  733. ;Divide
  734. INP12:    NEG    DX
  735. INP13:    CALL    FPDIV
  736.     DEC    DX
  737.     JNE    INP13
  738.     RET
  739. ;
  740. ADDIGIT:
  741.     CALL    FAC1_MBY10    ;Multiply mantissa by 10
  742. ;add in the digit
  743.     AND    AX,0FH
  744.     ADD    FAC1_MAN+8,AX
  745.     ADC    FAC1_MAN+6,0
  746.     ADC    FAC1_MAN+4,0
  747.     ADC    FAC1_MAN+2,0
  748.     ADC    FAC1_MAN,0
  749.     RET
  750.     
  751.  
  752. ;**********
  753. ;Floating-point output routine
  754. ;Stores the number in FAC1 into memory location
  755. ;pointed to by DI.  CX contains the number of digits
  756. ;to the right of the decimal place to output.
  757. ;Number is stored as a string:
  758. ;      sd.dddddddEsddd<Null>
  759. ; s is + or - and d is a digit.
  760. ;Note: the mantissa can hold 19 significant digits.
  761.     PUBLIC FPOUT
  762. FPOUT:
  763.     CLD
  764.     PUSH    CX        ;Save
  765. ;First see if we have true zero.  If so we can skip
  766. ;a lot.
  767.     CMP    FAC1_EXP,0
  768.     JZ    FOUT3
  769. ;Put a floating-point 10 in FAC2.  We'll use it later
  770.     XOR    AX,AX
  771.     MOV    FAC2_SIGN,AL
  772.     MOV    FAC2_MAN+2,AX
  773.     MOV    FAC2_MAN+4,AX
  774.     MOV    FAC2_MAN+6,AX
  775.     MOV    FAC2_MAN+8,AX
  776.     MOV    FAC2_MAN,5000H
  777.     MOV    FAC2_EXP,BIAS+4
  778. ;Use DX to hold the decimal exponent
  779.     XOR    DX,DX
  780. ;Divide by 10 while FAC1 is >15 (Check for this by watching
  781. ;the exponent).  For each division, increment DX by 1.
  782. FOUT1:    CMP    FAC1_EXP,BIAS+4
  783.     JBE    FOUT2
  784.     CALL    FPDIV
  785.     INC    DX
  786.     JMP    FOUT1
  787. ;Multiply by 10 while FAC1 is <1.  For each multiplication, 
  788. ;decrement DX by 1.
  789. FOUT2:    CMP    FAC1_EXP,BIAS+1
  790.     JA    FOUT2A
  791.     CALL    FPMULT
  792.     DEC    DX
  793.     JMP    FOUT2
  794. ;If number is greater than or equal to 10, do one more
  795. ;division by 10 to get things in line. And don't forget to
  796. ;increment DX.
  797. FOUT2A:
  798.     CMP    FAC1_EXP,BIAS+4
  799.     JNE    FOUT3
  800.     CMP    FAC1_MAN,5000H
  801.     JL    FOUT3
  802.     CALL    FPDIV
  803.     INC    DX
  804. ;Position binary point between bits 75 and 76 so we can
  805. ;isolate the integer portion of the floating point number.
  806. FOUT3:    CMP    FAC1_EXP,BIAS+4
  807.     JNE    FOUT4
  808.     SHL    FAC1_MAN+8,1
  809.     RCL    FAC1_MAN+6,1
  810.     RCL    FAC1_MAN+4,1
  811.     RCL    FAC1_MAN+2,1
  812.     RCL    FAC1_MAN,1
  813.     JMP    SHORT FOUT5
  814. FOUT4:    CMP    FAC1_EXP,BIAS+3
  815.     JGE    FOUT4A
  816.     SHR    FAC1_MAN,1
  817.     RCR    FAC1_MAN+2,1
  818.     RCR    FAC1_MAN+4,1
  819.     RCR    FAC1_MAN+6,1
  820.     RCR    FAC1_MAN+8,1
  821.     INC    FAC1_EXP
  822.     JMP    FOUT4
  823. ;Do any rounding here.  (I'm not happy with what I've got here...
  824. ;perhaps a better rounding method could be found.)
  825. FOUT4A:
  826.     ADD    FAC1_MAN+8,9
  827.     ADC    FAC1_MAN+6,0
  828.     ADC    FAC1_MAN+4,0
  829.     ADC    FAC1_MAN+2,0
  830.     ADC    FAC1_MAN,0
  831. ;Make sure we didn't overflow
  832.     CMP    FAC1_MAN,0A000H
  833.     JB    FOUT5
  834.     XOR    AX,AX
  835.     MOV    FAC1_MAN+8,AX
  836.     MOV    FAC1_MAN+6,AX
  837.     MOV    FAC1_MAN+4,AX
  838.     MOV    FAC1_MAN+2,AX
  839.     MOV    FAC1_MAN,1000H
  840.     INC    DX
  841. ;Save the sign
  842. FOUT5:    CMP    FAC1_SIGN,0
  843.     JNE    FOUT5A
  844.     MOV    BYTE PTR [DI],'+'
  845.     JMP    SHORT FOUT5B
  846. FOUT5A:    MOV    BYTE PTR [DI],'-'
  847. FOUT5B:    INC    DI
  848. ;Get leftmost digit
  849.     MOV    AL,BYTE PTR FAC1_MAN+1
  850.     SHR    AL,1
  851.     SHR    AL,1
  852.     SHR    AL,1
  853.     SHR    AL,1    ;Digit now in low nibble
  854.     ADD    AL,'0'
  855.     STOSB
  856. ;OUTPUT the decimal point
  857.     MOV    AL,'.'
  858.     STOSB
  859. ;Output the remaining digits.
  860.     POP    CX        ;Restore saved count
  861. FOUT6:    AND    FAC1_MAN,0FFFH    ;Strip integer portion
  862.     CALL    FAC1_MBY10    ;Multiply by 10
  863.     MOV    AL,BYTE PTR FAC1_MAN+1
  864.     SHR    AL,1        ;Get next integer part
  865.     SHR    AL,1
  866.     SHR    AL,1
  867.     SHR    AL,1
  868.     ADD    AL,'0'
  869.     STOSB
  870.     LOOP    FOUT6
  871. ;Get the exponent portion
  872.     MOV    AL,'E'
  873.     STOSB
  874.     OR    DX,DX
  875.     JS    FOUT7
  876.     MOV    AL,'+'
  877.     JMP    SHORT FOUT8
  878. FOUT7:    MOV    AL,'-'
  879.     NEG    DX        ;Absolute value of exp. in DX
  880. FOUT8:    STOSB
  881. ;Following routine gets the exponent value.  Note that we use
  882. ;division by 10 to strip off digits as remainders.  This means
  883. ;the number comes out in right-to-left order...backwards.  So
  884. ;we have to 'flip' the number when we're done.
  885.     MOV    AX,DX
  886.     OR    AX,AX        ;0 exponent?
  887.     JZ    FOUT10
  888.     MOV    SI,DI        ;Save location in buffer
  889.     MOV    CX,10
  890. FOUT9:    XOR    DX,DX        ;Clear for division
  891.     DIV    CX
  892. FOUT10:    ADD    DL,'0'        ;Digit in DX
  893.     MOV    [DI],DL
  894.     INC    DI
  895.     OR    AX,AX        ;Done?
  896.     JNE    FOUT9
  897.     MOV    BYTE PTR [DI],0    ;Store null
  898. ;Fix the exponent so it reads left-to-right.
  899.     DEC    DI
  900. FOUT11:    CMP    SI,DI        ;Finished flipping?
  901.     JAE    FOUT12
  902.     MOV    AL,[DI]        ;Swap
  903.     XCHG    AL,[SI]
  904.     MOV    [DI],AL
  905.     INC    SI
  906.     DEC    DI
  907.     JMP    FOUT11
  908. ;Exit
  909. FOUT12:    RET
  910.  
  911. ;**********
  912. ;Multiply contents of FAC1_MAN by 10.
  913. ;Do this using the formula: 10*x = 2*x + 8*x and
  914. ;the fact that you can multiply by 2 and multiply by
  915. ;8 by doing left shifts.
  916. ;Note that this only multiplies manissas...not the entire FP number.
  917. FAC1_MBY10:
  918.     PUSH    AX
  919.     SHL    FAC1_MAN+8,1
  920.     RCL    FAC1_MAN+6,1
  921.     RCL    FAC1_MAN+4,1
  922.     RCL    FAC1_MAN+2,1
  923.     RCL    FAC1_MAN,1
  924. ;
  925.     MOV    AX,FAC1_MAN
  926.     MOV    FAC3_MAN,AX
  927.     MOV    AX,FAC1_MAN+2
  928.     MOV    FAC3_MAN+2,AX
  929.     MOV    AX,FAC1_MAN+4
  930.     MOV    FAC3_MAN+4,AX
  931.     MOV    AX,FAC1_MAN+6
  932.     MOV    FAC3_MAN+6,AX
  933.     MOV    AX,FAC1_MAN+8
  934.     MOV    FAC3_MAN+8,AX
  935. ;
  936.     SHL    FAC1_MAN+8,1
  937.     RCL    FAC1_MAN+6,1
  938.     RCL    FAC1_MAN+4,1
  939.     RCL    FAC1_MAN+2,1
  940.     RCL    FAC1_MAN,1
  941. ;
  942.     SHL    FAC1_MAN+8,1
  943.     RCL    FAC1_MAN+6,1
  944.     RCL    FAC1_MAN+4,1
  945.     RCL    FAC1_MAN+2,1
  946.     RCL    FAC1_MAN,1
  947. ;
  948.     MOV    AX,FAC3_MAN+8
  949.     ADD    FAC1_MAN+8,AX
  950.     MOV    AX,FAC3_MAN+6
  951.     ADC    FAC1_MAN+6,AX
  952.     MOV    AX,FAC3_MAN+4
  953.     ADC    FAC1_MAN+4,AX
  954.     MOV    AX,FAC3_MAN+2
  955.     ADC    FAC1_MAN+2,AX
  956.     MOV    AX,FAC3_MAN
  957.     ADC    FAC1_MAN,AX
  958.     POP    AX
  959. ;
  960.     RET
  961.  
  962. #endasm
  963. 
  964.